using System;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraBars;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;
using CSW.Framework.Common;
using CSW.Framework.Common.Utils;

namespace CSW.Framework.UI.DevEx.UserControls
{
    /// <summary>
    /// DevEx Grid Control which supports row double clicking and right-click popup menu.
    /// </summary>
    public class DXGridControl : GridControl
    {
        #region <<< Private Fields >>>

        private static string m_ExportsDirectory;

        private readonly BarManager m_BarManager = new BarManager();
        private readonly PopupMenu m_PopupMenu;
        private readonly Timer m_PopupTimer = new Timer();

        private RowDoubleClickEventHandler m_RowDoubleClickHandler;
        private string m_ExportFileName;
        private int m_ClickTick;
        private BaseEdit m_ActiveEditor;
        private bool m_FirstClickFlag;
        private bool m_ViewEventsWired;

        #endregion <<< Private Fields >>>

        #region <<< Constructor >>>

        static DXGridControl()
        {
            if (!string.IsNullOrEmpty(PathUtils.LocalDataDirectory))
            {
                m_ExportsDirectory = Path.Combine(PathUtils.LocalDataDirectory, "Exports");
                Directory.CreateDirectory(m_ExportsDirectory);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DXGridControl"/> class.
        /// </summary>
        public DXGridControl()
        {
            ViewRemoved += BaseGridControl_ViewRemoved;

            m_PopupMenu = new PopupMenu(m_BarManager);
            BarSubItem x = new BarSubItem(m_BarManager, "Export");
            BarButtonItem y = new BarButtonItem(m_BarManager, "Export CSV");
            BarButtonItem z = new BarButtonItem(m_BarManager, "Export XLS");
            x.AddItem(y);
            x.AddItem(z);
            m_PopupMenu.AddItem(x);

            y.ItemClick += ExportCSVClick;
            z.ItemClick += ExportXLSClick;

            m_PopupTimer.Interval = 1000 * 5;
            m_PopupTimer.Tick += m_PopupTimer_Tick;
        }

        #endregion <<< Constructor >>>

        #region <<< Public Events >>>

        /// <summary>
        /// Occurs when a row is double clicked.
        /// </summary>
        public event RowDoubleClickEventHandler RowDoubleClick
        {
            add
            {
                WireViewEvents();
                m_RowDoubleClickHandler += value;
            }
            remove
            {
                m_RowDoubleClickHandler -= value;
            }
        }

        #endregion <<< Public Events >>>

        #region <<< Public Methods >>>

        /// <summary>
        /// Adds a button to the popup menu.
        /// </summary>
        /// <param name="text">The button text.</param>
        /// <returns>The button.</returns>
        public BarButtonItem AddPopupButton(string text)
        {
            BarButtonItem barButtonItem = new BarButtonItem(m_BarManager, text);
            m_PopupMenu.AddItem(barButtonItem);
            return barButtonItem;
        }

        /// <summary>
        /// Adds a sub menu to the popup menu.
        /// </summary>
        /// <param name="text">The sub menu text.</param>
        /// <returns>The sub menu.</returns>
        public BarSubItem AddPopupSubMenu(string text)
        {
            BarSubItem barSubItem = new BarSubItem(m_BarManager, text);
            m_PopupMenu.AddItem(barSubItem);
            return barSubItem;
        }

        #endregion <<< Public Methods >>>

        #region <<< Public Properties >>>

        /// <summary>
        /// Gets or sets the name of the export file.
        /// </summary>
        /// <value>The name of the export file.</value>
        public string ExportFileName
        {
            get { return m_ExportFileName; }
            set { m_ExportFileName = value; }
        }

        /// <summary>
        /// Gets the bar manager.
        /// </summary>
        /// <value>The bar manager.</value>
        public BarManager BarManager
        {
            get { return m_BarManager; }
        }

        /// <summary>
        /// Gets the popup menu.
        /// </summary>
        /// <value>The popup menu.</value>
        public PopupMenu PopupMenu
        {
            get { return m_PopupMenu; }
        }

        /// <summary>
        /// Gets or sets the export directory.
        /// </summary>
        /// <value>The export directory.</value>
        public static string ExportDirectory
        {
            get { return m_ExportsDirectory; }
            set { m_ExportsDirectory = value; }
        }

        #endregion <<< Public Properties >>>

        #region <<< Internal Methods >>>

        internal void WireViewEvents()
        {
            if (!m_ViewEventsWired)
            {
                foreach (GridView view in Views)
                {
                    view.MouseDown += gridView1_MouseDown;
                    view.MouseUp += gridView1_MouseUp;
                    view.ShownEditor += gridView1_ShownEditor;
                    view.HiddenEditor += gridView1_HiddenEditor;
                    view.DoubleClick += gridView1_DoubleClick;
                }
                m_ViewEventsWired = true;
            }
        }

        #endregion <<< Public Methods >>>

        #region <<< Private Methods >>>

        private void m_PopupTimer_Tick(object sender, EventArgs e)
        {
            m_PopupTimer.Enabled = false;
            m_PopupMenu.HidePopup();
        }

        private void ExportXLSClick(object sender, ItemClickEventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;

            GridView gridView = (GridView)DefaultView;
            string fileName = GetFullExportFileName("xls");
            gridView.ExportToXls(fileName);
            Process.Start(fileName);

            Cursor.Current = Cursors.Default;
        }

        private void ExportCSVClick(object sender, ItemClickEventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;

            GridView gridView = (GridView)DefaultView;

            DataTable dt = new DataTable();
            foreach (GridColumn column in gridView.Columns.Cast<GridColumn>().OrderBy(p => p.VisibleIndex).Where(p => p.VisibleIndex >= 0))
            {
                DataColumn dc = dt.Columns.Add();
                dc.ColumnName = column.Caption;
            }

            for (int i = 0; i < gridView.DataRowCount; i++)
            {
                DataRow dr = dt.NewRow();
                dt.Rows.Add(dr);

                int rowHandle = gridView.GetRowHandle(i);
                foreach (GridColumn column in gridView.Columns.Cast<GridColumn>().Where(p => p.VisibleIndex >= 0))
                {
                    Type columnType = column.ColumnType;

                    if (columnType == typeof(bool) || columnType == typeof(bool?))
                        dr[column.Caption] = (((bool?)gridView.GetRowCellValue(rowHandle, column) == true) ? "Y" : "");
                    else
                        dr[column.Caption] = gridView.GetRowCellDisplayText(rowHandle, column);
                }
            }

            Cursor.Current = Cursors.Default;

            ExportUtils.CreateCSV(dt, GetFullExportFileName("csv"), true, true, false, true, true);
        }

        private string GetFullExportFileName(string extension)
        {
            Guard.ArgumentNotNullOrEmptyString(m_ExportsDirectory, "m_ExportsDirectory");

            string fileName = Path.Combine(m_ExportsDirectory, string.Format(@"{0}Grid\{0}_{1}.{2}", m_ExportFileName, TimeStamp.GetCurrent(), extension));
            Directory.CreateDirectory(Path.GetDirectoryName(fileName));
            return fileName;
        }

        private void BaseGridControl_ViewRemoved(object sender, ViewOperationEventArgs e)
        {
            GridView view = e.View as GridView;
            if (view != null)
            {
                view.MouseDown -= gridView1_MouseDown;
                view.MouseUp -= gridView1_MouseUp;
                view.ShownEditor -= gridView1_ShownEditor;
                view.HiddenEditor -= gridView1_HiddenEditor;
                view.DoubleClick -= gridView1_DoubleClick;
            }
        }

        private static int GetClickedRow(GridView view, Point pt)
        {
            GridHitInfo hi = view.CalcHitInfo(pt);
            return hi.RowHandle;
        }

        private void SetTickCount(GridView view, MouseEventArgs e)
        {
            if (view.IsDataRow(GetClickedRow(view, new Point(e.X, e.Y))))
                m_ClickTick = Environment.TickCount;
            else
                m_ClickTick = 0;
        }

        private void gridView1_MouseDown(object sender, MouseEventArgs e)
        {
            GridView view = sender as GridView;
            if (view != null)
            {
                if (m_PopupMenu != null)
                {
                    m_PopupMenu.HidePopup();
                }

                if (view.OptionsBehavior.EditorShowMode == DevExpress.Utils.EditorShowMode.MouseUp)
                    return;

                if (e.Button == MouseButtons.Right)
                {
                    if (string.IsNullOrEmpty(m_ExportFileName))
                        return;

                    if (m_PopupMenu != null)
                    {
                        int rowHandle = GetClickedRow(view, PointToClient(Cursor.Position));
                        if (view.IsDataRow(rowHandle))
                        {
                            m_PopupMenu.ShowPopup(MousePosition);
                            m_PopupTimer.Enabled = false;
                            m_PopupTimer.Enabled = true;
                        }
                    }
                    return;
                }

                SetTickCount(view, e);
            }
        }

        private void gridView1_MouseUp(object sender, MouseEventArgs e)
        {
            GridView view = sender as GridView;
            if (view != null)
            {
                if (view.OptionsBehavior.EditorShowMode != DevExpress.Utils.EditorShowMode.MouseUp) return;
                SetTickCount(view, e);
            }
        }

        private void gridView1_ShownEditor(object sender, EventArgs e)
        {
            GridView view = sender as GridView;
            if (view != null)
            {
                if ((Environment.TickCount - m_ClickTick) < SystemInformation.DoubleClickTime)
                {
                    if (view.OptionsBehavior.EditorShowMode != DevExpress.Utils.EditorShowMode.MouseUp)
                        m_FirstClickFlag = true;
                    m_ActiveEditor = view.ActiveEditor;
                    m_ActiveEditor.MouseDown += Editor_MouseDown;
                }
            }
        }

        private void Editor_MouseDown(object sender, MouseEventArgs e)
        {
            BaseEdit editor = sender as BaseEdit;
            if (editor != null)
            {
                if ((Environment.TickCount - m_ClickTick) < SystemInformation.DoubleClickTime)
                {
                    if (m_FirstClickFlag)
                    {
                        m_FirstClickFlag = false;
                        return;
                    }

                    GridControl grid = editor.Parent as GridControl;
                    if (grid != null)
                    {
                        GridView view = grid.FocusedView as GridView;
                        if (view != null)
                        {
                            int rowHandle = view.FocusedRowHandle;
                            FireRowDoubleClick(view, rowHandle);
                        }
                    }
                }
                m_ClickTick = Environment.TickCount;
            }
        }

        private void gridView1_HiddenEditor(object sender, EventArgs e)
        {
            if (m_ActiveEditor != null)
            {
                m_ActiveEditor.MouseDown -= Editor_MouseDown;
                m_ActiveEditor = null;
            }
        }

        private void gridView1_DoubleClick(object sender, EventArgs e)
        {
            // This event occurs when 
            // 1)the row indicators and lines between cells are double-clicked;
            // 2)a cell is double-clicked and only if its in-place editor is not activated by the first click.
            GridView view = sender as GridView;
            if (view != null)
            {
                int rowHandle = GetClickedRow(view, PointToClient(Cursor.Position));
                if (view.IsDataRow(rowHandle))
                    FireRowDoubleClick(view, rowHandle);
            }
        }

        private void FireRowDoubleClick(GridView view, int rowHandle)
        {
            RowDoubleClickEventHandler rowDoubleClick = m_RowDoubleClickHandler;
            if (rowDoubleClick != null)
            {
                rowDoubleClick(this, new RowDoubleClickEventArgs(view, rowHandle));
            }
        }

        #endregion <<< Private Methods >>>
    }
}